package business.services;

import java.util.List;
import java.util.Vector;
import common.Contexto;
import business.interfaces.IClase;
import business.interfaces.ICuota;
import business.interfaces.IFicha;
import business.interfaces.ISocio;
import data.entities.Cuota;
import data.entities.Ficha;
import data.entities.Socio;
import data.entities.TipoPago;
import data.entities.TipoPagoEnum;
import data.repository.IRepository;
import data.repository.QueryParameter;
import data.repository.Repository;
import data.repository.RepositoryException;

public class ServicioCuentas implements IServicioCuentas {
	private IRepository<Ficha> repositorioCuentas;
	private IRepository<TipoPago> repositorioTiposPago;
	private IServicioClientes servicioClientes;
	private IServicioClases servicioClases;
	
	public ServicioCuentas(IServicioClientes servicioClientes, IServicioClases servicioClases) {
		this.repositorioCuentas = new Repository<Ficha>();
		this.repositorioTiposPago = new Repository<TipoPago>();
		this.servicioClientes = servicioClientes;
		this.servicioClases = servicioClases;
	}
	
	public IFicha getFicha(String dni) throws ServiceException {
		try {
			return this.getFichaExistente(dni);	
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
	}

	public Boolean esMoroso(String dni) throws ServiceException {
		try {
			Ficha ficha = this.getFichaExistente(dni);
			
			if(ficha == null) {
				String mensaje = String.format("No existe ficha para el socio %s", dni);
				
				throw new ServiceException(mensaje);
			}
			
			return this.esMoroso(ficha);
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
	}
	
	public List<IFicha> getMorosos() throws ServiceException {
		List<Ficha> fichas;
		
		try {
			fichas = this.repositorioCuentas.getAll(Ficha.class);
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
		
		List<IFicha> morosos = new Vector<IFicha>();
		
		for(Ficha ficha : fichas) {
			if(this.esMoroso(ficha)) {
				morosos.add(ficha);
			}
		}
		
		return morosos;
	}
	
	public void crearFicha(String dni) throws ServiceException {
		Socio socioExistente = (Socio) this.servicioClientes.getSocio(dni);
		Ficha newFicha = new Ficha();
		
		newFicha.setSocio(socioExistente);
		newFicha.setActivo(true);
		newFicha.setAprobada(true);
		
		try {
			this.repositorioCuentas.saveOrUpdate(newFicha);
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
	}

	public Integer getUltimoMesPago(String dni) throws ServiceException {
		IFicha ficha = this.getFicha(dni);
		Integer ultimoAnioPago = -1;
		Integer ultimoMesPago = -1;
		
		for(ICuota cuota : ficha.getCuotas()) {
			if(!cuota.getPendientePago() && cuota.getAnio() > ultimoAnioPago) {
				ultimoAnioPago = cuota.getAnio();
				ultimoMesPago = cuota.getMes();
			} else if (!cuota.getPendientePago() && cuota.getAnio() == ultimoAnioPago) {
				if(cuota.getMes() > ultimoMesPago) {
					ultimoMesPago = cuota.getMes();
				}
			}
		}
		
		return ultimoMesPago;
	}

	public void abonarDeuda(String dni, TipoPagoEnum tipoPagoEnum) throws ServiceException {
		try {
			TipoPago tipoPago = this.getTipoPago(tipoPagoEnum);
			Ficha fichaExistente = this.getFichaExistente(dni);
			
			if(fichaExistente == null) {
				String mensaje = String.format("No existe ficha para el socio %s", dni);
				
				throw new ServiceException(mensaje);
			}
			
			for(ICuota cuota : fichaExistente.getCuotas()) {
				if(cuota.getPendientePago()) {
					((Cuota) cuota).setPendientePago(false);
					((Cuota) cuota).setTipoPago(tipoPago);
				}
			}
			
			this.repositorioCuentas.saveOrUpdate(fichaExistente);
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
	}

	public void abonarDeuda(String dni, Integer anio, Integer mes, TipoPagoEnum tipoPagoEnum) throws ServiceException {
		try {
			TipoPago tipoPago = this.getTipoPago(tipoPagoEnum);
			Ficha fichaExistente = this.getFichaExistente(dni);
			
			if(fichaExistente == null) {
				String mensaje = String.format("No existe ficha para el socio %s", dni);
				
				throw new ServiceException(mensaje);
			}
			
			for(ICuota cuota : fichaExistente.getCuotas()) {
				if(cuota.getPendientePago() && cuota.getAnio() == anio && cuota.getMes() == mes) {
					((Cuota) cuota).setPendientePago(false);
					((Cuota) cuota).setTipoPago(tipoPago);
				}
			}
			
			this.repositorioCuentas.saveOrUpdate(fichaExistente);
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
	}

	public void suspenderSocio(String dni) throws ServiceException {
		try {
			Ficha fichaExistente = this.getFichaExistente(dni);
			
			if(fichaExistente == null) {
				String mensaje = String.format("No existe ficha para el socio %s", dni);
				
				throw new ServiceException(mensaje);
			}
			
			fichaExistente.setActivo(false);			
			this.repositorioCuentas.saveOrUpdate(fichaExistente);
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}
	}

	public void imputarCuotasSocios() throws ServiceException {
		List<ISocio> socios = this.servicioClientes.getSocios();
		Integer anioActual = Contexto.getAnioActual();
		Integer mesActual = Contexto.getMesActual();
		
		try {
			for(ISocio socio : socios) {
				String dni = socio.getDni();
				Ficha ficha = this.getFichaExistente(dni);
				
				if(ficha == null) {
					String mensaje = String.format("No existe ficha para el socio %s", dni);
					
					throw new ServiceException(mensaje);
				}
				
				IClase clase = this.servicioClases.getClase(dni);
				Double totalAPagar = Contexto.PrecioPorMembresia;
				
				if(clase != null){
					totalAPagar += Contexto.PrecioPorClase * clase.getCantidadPorMes();
				}
				
				Cuota newCuota = new Cuota();
				
				newCuota.setFicha(ficha);
				newCuota.setAnio(anioActual);
				newCuota.setMes(mesActual);
				newCuota.setTotalPago(totalAPagar);
				newCuota.setPendientePago(true);
				
				ficha.addCouta(newCuota);
				
				this.repositorioCuentas.saveOrUpdate(ficha);
			}
		} catch (RepositoryException e) {
			throw new ServiceException(e);
		}	
	}

	@SuppressWarnings("unused")
	public void notificarMorosos() throws ServiceException {
		List<IFicha> morosos = this.getMorosos();
		
		for(IFicha moroso : morosos) {
			//TODO: Aca se deberia enviar un mail o notificar de alguna manera. No entra en en la demo creo
		}
	}
	
	private Ficha getFichaExistente(String dni) throws RepositoryException {
		String queryExpression = "from Ficha f where f.socio.cliente.dni = :dni";
		QueryParameter parameter = new QueryParameter("dni", dni);
		Ficha ficha = this.repositorioCuentas.get(queryExpression, parameter);
		
		return ficha;
	}
	
	private Boolean esMoroso(Ficha ficha) {
		Integer anioActual = Contexto.getAnioActual();
		Integer mesActual = Contexto.getMesActual();
		Boolean esMoroso = true;
		
		for(ICuota cuota : ficha.getCuotas()) {
			if(cuota.getAnio() == anioActual && cuota.getMes() == mesActual && !cuota.getPendientePago()) {
				esMoroso = false;
				break;
			}
		}
		
		return esMoroso;
	}
	
	private TipoPago getTipoPago(TipoPagoEnum tipo) throws RepositoryException {
		String descripcion = tipo.name();
		String queryExpression = "from TipoPago t where t.descripcion = :descripcion";
		QueryParameter parameter = new QueryParameter("descripcion", descripcion);
		TipoPago tipoPago = this.repositorioTiposPago.get(queryExpression, parameter);
		
		return tipoPago;	
	}
}
